﻿using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Refit;
using Swashbuckle.AspNetCore.SwaggerGen;
using Syspetro.Core;
using Syspetro.Core.CommandBus;
using Syspetro.Core.DynamicApi;
using Syspetro.Core.Encrypt;
using Syspetro.Core.EventBus;
using Syspetro.Core.Extensions;
using Syspetro.Core.HttpService;
using Syspetro.Core.Localization;
using Syspetro.Core.SysInterface;
using Syspetro.Deploy.Jwt;
using Syspetro.Deploy.Managers;
using Syspetro.Deploy.Refit;
using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Syspetro.Deploy
{
    public static class ServiceCollection
    {
        public static IServiceCollection AddSyspetro(this IServiceCollection services, string mainNamespace = null)
        {
            services.AddControllers().AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver();//json字符串大小写原样输出
                options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); //序列化时key为驼峰样式
                options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
                options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;//忽略循环引用
            });
            services.AddTransient<AuthHeaderHandler>();
            services.AddCommandMap(mainNamespace);
            services.AddEventBus(mainNamespace);
            services.AddLocalizationLanguages();
            services.AddMemoryCache();
            services.AddSwagger(InternalApp.IsAuth);
            services.AddDataService(mainNamespace);
            services.AddObjectMapper(mainNamespace);
            services.AddDynamicWebApi(InternalApp.ApiPrefix);
            if (InternalApp.IsAuth)
            {
                services.AddAuthManager();
                services.AddJwtRS();
            }
            services.AddCorsAllowAll();
            services.AddRefit(mainNamespace);
            return services;
        }
        /// <summary>
        /// 添加身份信息获取
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IServiceCollection AddAuthManager(this IServiceCollection services)
        {
            services.AddScoped<IAuthorizationManager, AuthorizationManager>();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            return services;
        }
        /// <summary>
        /// 添加Swagger配置
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IServiceCollection AddSwagger(this IServiceCollection services, bool isAuth = true)
        {
            services.AddSwaggerGen(c =>
            {
                foreach (var group in DynamicApiGroups.GroupExtraInfos)
                {
                    c.SwaggerDoc(group.Group, new OpenApiInfo { Title = group.Title, Version = group.Version });
                }
                c.DocInclusionPredicate((currentGroup, apiDescription) =>
                {
                    if (apiDescription.TryGetMethodInfo(out var method))
                        return DynamicApiGroups.GetActionGroups(method).Any(u => u.Group == currentGroup);
                    return false;
                });
                if (isAuth)
                {
                    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
                    {
                        Description = "<b>添加Token：Bearer {your JWT token} <br/>授权地址:/api/AuthenticationApp/Login</b>",
                        Name = "Authorization",
                        In = ParameterLocation.Header,
                        Type = SecuritySchemeType.ApiKey,
                        BearerFormat = "JWT",
                        Scheme = "Bearer"
                    });
                    c.AddSecurityRequirement(new OpenApiSecurityRequirement {{
                            new OpenApiSecurityScheme{ Reference =new OpenApiReference{  Type = ReferenceType.SecurityScheme,Id = "Bearer"}},
                            Array.Empty<string>()
                    }});
                }
                var baseDirectory = System.AppDomain.CurrentDomain.BaseDirectory;
                DirectoryInfo dir = new(baseDirectory);
                FileInfo[] files = dir.GetFiles("*.xml");
                foreach (var fil in files)
                {
                    c.IncludeXmlComments(fil.FullName, true);
                }
            });
            return services;
        }
        /// <summary>
        /// 跨域设置
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IServiceCollection AddCorsAllowAll(this IServiceCollection services)
        {
            services.AddCors(c =>
            {
                c.AddPolicy("allowAll", policy =>
                {
                    policy
                    .AllowAnyOrigin()
                    .AllowAnyHeader()//Ensures that the policy allows any header.
                    .AllowAnyMethod();
                });
            });
            return services;
        }
        /// <summary>
        /// 添加Mapper对象映射
        /// </summary>
        /// <param name="services">服务集合</param>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        public static IServiceCollection AddObjectMapper(this IServiceCollection services, string AssemblyMatch = null)
        {
            var referencedAssemblies = AssemblyHelper.LoadFroms(AssemblyMatch);
            TypeAdapterConfig.GlobalSettings.Scan(referencedAssemblies.ToArray());
            // 配置默认全局映射（支持覆盖）
            TypeAdapterConfig.GlobalSettings.Default
                .NameMatchingStrategy(NameMatchingStrategy.Flexible)
                .PreserveReference(true);

            return services;
        }
        /// <summary>
        /// 自动注入CommandMap
        /// </summary>
        /// <param name="services"></param>
        /// <param name="AssemblyMatch"></param>
        /// <returns></returns>
        public static IServiceCollection AddCommandMap(this IServiceCollection services, string AssemblyMatch = null)
        {
            var referencedAssemblies = AssemblyHelper.LoadFroms(AssemblyMatch);
            var TypeICommand = typeof(ICommand);
            var TypeICommandHandler = typeof(ICommandHandler<>);
            var ss = referencedAssemblies.SelectMany(o => o.GetTypes());
            var Commands = referencedAssemblies
                .SelectMany(a => a.DefinedTypes)
                .Select(type => type.AsType())
                .Where(x => x != TypeICommand && TypeICommand.IsAssignableFrom(x)).ToList();

            var CommandHandlers = referencedAssemblies
                .SelectMany(a => a.DefinedTypes)
                .Select(type => type.AsType())
                .Where(t => t.GetInterfaces().Any(a => a.GetTypeInfo().IsGenericType && a.GetGenericTypeDefinition() == TypeICommandHandler))
                .ToList();

            var ICommands = Commands.Where(t => t.IsInterface).ToList();
            var interfaceTypes = CommandHandlers.Where(t => t.IsInterface).ToList();

            var _Commands = Commands.Where(t => t.IsClass).ToList();
            var _CommandHandlers = CommandHandlers.Where(t => t.IsClass).ToList();

            foreach (var implementType in _CommandHandlers)
            {
                if (typeof(IScopeService).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddScoped(interfaceType, implementType);
                }
                else if (typeof(ISingletonService).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddSingleton(interfaceType, implementType);
                }
                else if (typeof(ITransientService).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddTransient(interfaceType, implementType);
                }
            }

            foreach (var comd in _Commands)
            {
                string key = comd.FullName;
                var ch = interfaceTypes.Where(t => t.GetInterfaces().Any(a => a.GetGenericArguments().Any(aa => aa == comd))).ToList();
                if (ch != null)
                    CommandMap.Add(key, ch);
            }
            services.AddTransient<ICommandBus, CommandBus>();

            services.AddHttpClient();
            services.AddTransient<HttpHelper>();
            services.AddTransient<IHttpCommandHandler, HttpCommandHandler>();
            services.AddTransient<IPswdCheckCommandHandler, PswdCheckCommandHandler>();
            services.AddTransient<IPswdMd5CommandHandler, PswdMd5CommandHandler>();
            CommandMap.Add(typeof(HttpCommand).FullName, typeof(IHttpCommandHandler));
            CommandMap.Add(typeof(PswdCheckCommand).FullName, typeof(IPswdCheckCommandHandler));
            CommandMap.Add(typeof(PswdMd5Command).FullName, typeof(IPswdMd5CommandHandler));

            return services;
        }
        /// <summary>
        /// 自动注入EventBus
        /// </summary>
        /// <param name="services"></param>
        /// <param name="AssemblyMatch"></param>
        /// <returns></returns>
        public static IServiceCollection AddEventBus(this IServiceCollection services, string AssemblyMatch = null)
        {
            var referencedAssemblies = AssemblyHelper.LoadFroms(AssemblyMatch);
            var TypeICommand = typeof(IEventBase);
            var TypeICommandHandler = typeof(IEventHandler<>);
            var ss = referencedAssemblies.SelectMany(o => o.GetTypes());
            var Commands = referencedAssemblies
                .SelectMany(a => a.DefinedTypes)
                .Select(type => type.AsType())
                .Where(x => x != TypeICommand && TypeICommand.IsAssignableFrom(x)).ToList();

            var CommandHandlers = referencedAssemblies
                .SelectMany(a => a.DefinedTypes)
                .Select(type => type.AsType())
                .Where(t => t.GetInterfaces().Any(a => a.GetTypeInfo().IsGenericType && a.GetGenericTypeDefinition() == TypeICommandHandler))
                .ToList();

            var ICommands = Commands.Where(t => t.IsInterface).ToList();
            var ICommandHandlers = CommandHandlers.Where(t => t.IsInterface).ToList();

            var _Commands = Commands.Where(t => t.IsClass).ToList();
            var _CommandHandlers = CommandHandlers.Where(t => t.IsClass).ToList();

            foreach (var comd in _CommandHandlers)
            {
                var interfaceType = ICommandHandlers.FirstOrDefault(x => x.IsAssignableFrom(comd));
                if (interfaceType != null)
                    services.AddTransient(interfaceType, comd);
            }

            foreach (var comd in _Commands)
            {
                string key = comd.FullName;
                var chs = ICommandHandlers.Where(t => t.GetInterfaces().Any(a => a.GetGenericArguments().Any(aa => aa == comd))).ToList();
                foreach (var ch in chs)
                {
                    var atr = ch.GetCustomAttributes(true).Where(t => t.GetType() == typeof(EventBasAttribute)).ToList();
                    if (atr?.Count > 0)
                    {
                        var Group = atr.FirstOrDefault() as EventBasAttribute;
                        EventSchedulerDefault.Add(comd, ch, Group.Group);
                    }
                    else
                    {
                        EventSchedulerDefault.Add(comd, ch);
                    }
                }
            }
            return services;
        }
        /// <summary>
        /// 自动注入IOC
        /// </summary>
        /// <param name="services"></param>
        /// <param name="AssemblyMatch"></param>
        /// <returns></returns>
        public static IServiceCollection AddDataService(this IServiceCollection services, string AssemblyMatch = null)
        {
            var referencedAssemblies = AssemblyHelper.LoadFroms(AssemblyMatch);
            var baseType = typeof(IService);
            var ss = referencedAssemblies.SelectMany(o => o.GetTypes());

            var types = referencedAssemblies
                .SelectMany(a => a.DefinedTypes)
                .Select(type => type.AsType())
                .Where(x => x != baseType && baseType.IsAssignableFrom(x)).ToList();
            var implementTypes = types.Where(x => x.IsClass).ToList();
            var interfaceTypes = types.Where(x => x.IsInterface).ToList();
            foreach (var implementType in implementTypes)
            {
                if (typeof(IScopeService).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddScoped(interfaceType, implementType);
                }
                else if (typeof(ISingletonService).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddSingleton(interfaceType, implementType);
                }
                else if (typeof(ITransientService).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddTransient(interfaceType, implementType);
                }
            }
            return services;
        }

        public static IServiceCollection AddRefit(this IServiceCollection services, string AssemblyMatch = null)
        {
            var referencedAssemblies = AssemblyHelper.LoadFroms(AssemblyMatch);
            var ss = referencedAssemblies.SelectMany(o => o.GetTypes());

            var assembly = ss
                .Where(x => x.GetInterfaces().Contains(typeof(IRemoteService))&&x.IsInterface).ToList();
            foreach (Type remoteservice in assembly)
            {
                BaseAddressAttribute baseAddress = remoteservice.GetCustomAttributes<BaseAddressAttribute>().FirstOrDefault<BaseAddressAttribute>();
                if (baseAddress == null)
                {
                    throw new Exception();
                }
                Uri uri = new(InternalApp.Configuration.GetSection($"Remoteconfig:{baseAddress.ServiceName}").Value);

                services.AddRefitClient(remoteservice)
                  .ConfigureHttpClient(c =>
                  {
                      c.BaseAddress = uri;
                  }).AddHttpMessageHandler<AuthHeaderHandler>();
            }
            return services;
        }
        public static IServiceCollection AddRefit(this IServiceCollection services, IConfiguration configuration, string AssemblyMatch = null)
        {
            var referencedAssemblies = AssemblyHelper.LoadFroms(AssemblyMatch);
            var ss = referencedAssemblies.SelectMany(o => o.GetTypes());

            var assembly = ss
                .Where(x => x.GetInterfaces().Contains(typeof(IRemoteService)) && x.IsInterface).ToList();
            foreach (Type remoteservice in assembly)
            {
                BaseAddressAttribute baseAddress = remoteservice.GetCustomAttributes<BaseAddressAttribute>().FirstOrDefault<BaseAddressAttribute>();
                if (baseAddress == null)
                {
                    throw new Exception();
                }
                Uri uri = new(configuration.GetSection($"Remoteconfig:{baseAddress.ServiceName}").Value);

                services.AddRefitClient(remoteservice)
                  .ConfigureHttpClient(c =>
                  {
                      c.BaseAddress = uri;
                  }).AddHttpMessageHandler<AuthHeaderHandler>();
            }
            return services;
        }

    }
}
